/***********************************************************************************
Copy right:	    hqyj Tech.
Author:         jiaoyue
Date:           2023.07.01
Description:    http请求处理
***********************************************************************************/
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "custom_handle.h"
#include <unistd.h>
#include <stdio.h>
#include <modbus.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "cJSON.h"
#include "shmem.h"

#define KB 1024
#define HTML_SIZE (64 * KB)

int mymodbusset(int sock, cJSON *root, int key);

// 普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \
                  "Connection: close\r\n"

extern struct shm_param para; // 共享内存的首地址
extern int *total;            // 指向共享内存中数据节点总个数  6  4
extern data_t *node_arr;      // 指向共享内存中节点缓存数组头
extern void *node_p;

/**
 * 处理post的请求数据中的登录请求
 * 参数:accepted 套接字  post的请求数据
 * 返回值: int类型的数值
 */
static int handle_login(int sock, const char *input)
{
    char reply_buf[HTML_SIZE] = {0};
    //  字符数组 的 大小 64 kb
    char *uname = strstr(input, "username=");
    /**
     * char *strstr(const char *haystack, const char *needle);
     * 函数确实返回指向 haystack 中第一次出现的 needle 字符串的指针，如果没有找到，则返回 NULL。
     */
    uname += strlen("username=");
    // 移动指针位置到 实际用户名数据的首地址
    char *p = strstr(input, "password=");
    // 将第一次出现password字符串位置的前一个字节设置成 \0  （也就是将 & 符 变成 \0）
    // 到这个分割符时%s结束
    // username=JohnDoe&password=password123

    *(p - 1) = '\0';
    printf("username = %s\n", uname); // 打印输入的用户名

    // 移动指针位置到 实际密码数据的首地址
    char *passwd = p + strlen("password=");

    printf("passwd = %s\n", passwd); // 打印输入的密码

    // 对账号和密码进行比对：
    if (strcmp(uname, "admin") == 0 && strcmp(passwd, "123") == 0)
    {
        const char *header = "HTTP/1.1 200 OK\r\n"
                             "Content-Type: text/html; charset=utf-8\r\n"
                             "\r\n"; // 空行分隔头和内容

        // 存储用户名到localStorage（可选，用于新页面识别用户）
        sprintf(reply_buf, "<script>localStorage.setItem('username', '%s');</script>", uname);
        // 跳转到新网页（使用绝对路径）
        strcat(reply_buf, "<script>window.location.href = './index.html';</script>");

        // 3. 先发送响应头，再发送响应内容
        send(sock, header, strlen(header), 0);
        send(sock, reply_buf, strlen(reply_buf), 0);
        printf("_____%d\n", __LINE__);
        // 如果用户名和密码都匹配，则构造一个HTML脚本字符串，
        // 使用localStorage.setItem将用户名存储在浏览器的本地存储中，并通过JavaScript重定向到/index.html页面。
    }
    else
    {
        printf("web login failed\n");
        //"用户名或密码错误"提示，chrome浏览器直接输送utf-8字符流乱码，没有找到太好解决方案，先过渡
        char out[128] = {0xd3, 0xc3, 0xbb, 0xa7, 0xc3, 0xfb, 0xbb, 0xf2, 0xc3,
                         0xdc, 0xc2, 0xeb, 0xb4, 0xed, 0xce, 0xf3};
        sprintf(reply_buf, "<script charset='gb2312'>alert('%s');</script>", out);
        strcat(reply_buf, "<script>window.location.href = '/login.html';</script>");
        send(sock, reply_buf, strlen(reply_buf), 0);
        // 如果用户名或密码不匹配，则打印一条失败消息到服务器端的控制台，
        // 并尝试向客户端发送一个包含错误提示的HTML脚本字符串。
    }

    return 0;
}

/**
 * 功能:执行post请求数据对应的求和函数
 * 参数:accepted 套接字  post的请求数据
 * 返回值: int类型
 */
static int handle_add(int sock, const char *input)
{
    int number1, number2;
    // input必须是"data1=1data2=6"类似的格式，注意前端过来的字符串会有双引号

    // 拆找出来的原始请求数据  req_buf = "data1=1data2=2"
    sscanf(input, "\"data1=%ddata2=%d\"", &number1, &number2);
    printf("num1 = %d\n", number1); // 1
    printf("num2 = %d\n", number2); // 2
    // 使用 sscanf 函数尝试从 input 字符串中按照 "data1=%ddata2=%d" 的格式
    // 解析出两个整数 number1 和 number2
    char reply_buf[HTML_SIZE] = {0};
    printf("num = %d\n", number1 + number2);     // int类型的 3
    sprintf(reply_buf, "%d", number1 + number2); // 3
    printf("resp = %s\n", reply_buf);            // 字符串类型的"3"
    send(sock, reply_buf, strlen(reply_buf), 0);
    return 0;
}

/**
 * 功能用于将int类型的数据挂树
 * 无返回值
 */
void aitooi(cJSON *item, cJSON *root, const char *key, int val)
{
    item = cJSON_CreateNumber(val);
    cJSON_AddItemToObject(root, key, item);
}

/**
 * 功能用于将float类型的数据挂树
 * 无返回值
 */
void aitoof(cJSON *item, cJSON *root, const char *key, float val)
{
    item = cJSON_CreateNumber(val);
    cJSON_AddItemToObject(root, key, item);
}

/**
 * 功能用于读取共享内存中的数据,转换成json格式上传给网页客户端口
 * 参数:accepted 套接字
 */
int get_jsondata(int sock, int opt, cJSON *root)
{
    printf("开始读取共享内存\n");
    char buff[1024] = "";
    FILE *fd = fopen("/home/hq/work/007/data_report/config.json", "r");
    if (NULL == fd)
    {
        perror("err!");
        return -1;
    }
    fread(buff, 4024, 1, fd);
    cJSON *root2 = cJSON_Parse(buff);                     // 根节点
    cJSON *mb_dev = cJSON_GetObjectItem(root2, "mb_dev"); // mb_dev节点
    cJSON *addr = cJSON_GetObjectItem(mb_dev, "addr");
    printf("%s:%s\n", addr->string, addr->valuestring);
    cJSON *port = cJSON_GetObjectItem(mb_dev, "port");
    printf("%s:%d\n", port->string, port->valueint);
    cJSON *stm32 = cJSON_GetObjectItem(root2, "stm32");
    // printf("------------%d\n", __LINE__);
    cJSON *data = cJSON_GetObjectItem(stm32, "data");
    // printf("------------%d\n", __LINE__);
    // 获取data数组节点的元素个数
    int stm32_num = cJSON_GetArraySize(data);
    printf("%d\n", stm32_num);
    cJSON *modbus = cJSON_GetObjectItem(root2, "modbus");
    cJSON *data1 = cJSON_GetObjectItem(modbus, "data");
    int modbus_num = cJSON_GetArraySize(data1);
    printf("%d\n", modbus_num);
    int sum = stm32_num + modbus_num;
    size_t shm_size = sizeof(int) + sizeof(data_t) * sum;
    shm_init(&para, "shm", shm_size);
    // 拿到共享内存首地址
    void *shm_base_addr = shm_getaddr(&para);
    data_t *node_arr = (struct data_t *)((char *)shm_base_addr + sizeof(int));
    printf("----------------%d", __LINE__);
    // total = (int *)(node_p + 4 + 6 * STD_NODE_LEN);
    printf("共享内存原始数据: %d %d %d %.2f %.2f %d %d %d %d %d %d\n", node_arr[0].new_val.b_val,
           node_arr[1].new_val.b_val, node_arr[2].new_val.b_val, node_arr[3].new_val.f_val,
           node_arr[4].new_val.f_val, node_arr[5].new_val.b_val, node_arr[6].new_val.b_val, node_arr[7].new_val.b_val, node_arr[8].new_val.b_val, node_arr[9].new_val.b_val, node_arr[10].new_val.b_val);
    cJSON *root1 = cJSON_CreateObject();
    cJSON *value = NULL;
    if (opt == 101)
    {
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(node_arr[0].new_val.f_val));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
    }
    if (opt == 102) // 蜂鸣器
    {
        cJSON *value1 = cJSON_GetObjectItem(root, "value");
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(value1->valueint));

        char *p = cJSON_Print(root1); // 也是响应正文
        int content_length = strlen(p);
        printf("等待上传的JSON格式的字符串__响应正文: %s\n", p);
        // char response[256];
        // sprintf(response,
        //         "HTTP/1.1 200 OK\r\n"                               // 状态行
        //         "Content-Type: application/json; charset=utf-8\r\n" // 响应头部：指定 JSON 格式
        //         "Content-Length: %d\r\n"                            // 响应头部：正文长度
        //         "Cache-Control: no-cache\r\n"              // 新增的缓存控制头，按需改策略
        //         "x-content-type-options: nosniff\r\n"       // 新增的响应头
        //         "Connection: close\r\n"                             // 响应头部：关闭连接
        //         "\r\n"                                              // 空行：分隔头部和正文
        //         "%s",                                               // 响应正文
        //         content_length,
        //         p);
        send(sock, p, strlen(p), 0);

        mymodbusset(sock, root1, opt);
        printf("____%d\n", __LINE__);
    }
    if (opt == 103) // 照明
    {
        cJSON *value1 = cJSON_GetObjectItem(root, "value");
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(value1->valueint));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
        mymodbusset(sock, root1, opt);
    }
    if (opt == 104)
    {
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(node_arr[3].new_val.f_val));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
    }
    if (opt == 105)
    {
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(node_arr[4].new_val.f_val));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
    }
    if (opt == 106) // 风扇
    {
        cJSON *value1 = cJSON_GetObjectItem(root, "value");
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(value1->valueint));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
        mymodbusset(sock, root1, opt);
    }
    if (opt == 107) // 水泵马达
    {
        cJSON *value1 = cJSON_GetObjectItem(root, "value");
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(value1->valueint));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
        mymodbusset(sock, root1, opt);
    }
    if (opt == 108) // 火焰
    {
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(node_arr[7].new_val.f_val));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
    }
    if (opt == 109) // 空气质量
    {
        cJSON_AddItemToObject(root1, "value", cJSON_CreateNumber(node_arr[8].new_val.f_val));
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
    }
    if (opt == 301) // 闸门
    {
        cJSON *value1 = cJSON_GetObjectItem(root, "value");
        cJSON_AddNumberToObject(root1, "value", value1->valueint);
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
        mymodbusset(sock, root1, opt);
    }
    if (opt == 302) // 备用电源
    {
        cJSON *value1 = cJSON_GetObjectItem(root, "value");
        cJSON_AddNumberToObject(root1, "value", value1->valueint);
        char *p = cJSON_Print(root1);
        printf("等待上传的JSON格式的字符串: %s\n", p);
        send(sock, p, strlen(p), 0);
        mymodbusset(sock, root1, opt);
    }
    // 内存清理
    cJSON_Delete(root1);
    return 1;
}

/**
 * 解析传来的json格式的post请求数据,并将其转换成消息队列,实现对设备层的控制
 * 参数:accepted 套接字  , cjson 的根节点
 */
int mymodbusset(int sock, cJSON *root, int key)
{
    // printf("解析网页端传过来的类型为2的JSON 命令\n");
    // // 从root节点获取login节点
    cJSON *value = cJSON_GetObjectItem(root, "value");
    cJSON *gen = cJSON_CreateObject();
    if (gen == NULL)
    {
        printf("创建 gen JSON 对象失败\n");
        return 1;
    }
    // 向 gen 对象添加 "type" 键值对，类型为 number，值为 2
    cJSON_AddNumberToObject(gen, "type", 2);
    // 创建 "data" 对应的 JSON 对象
    cJSON *dataObj = cJSON_CreateObject();
    if (dataObj == NULL)
    {
        printf("创建 data JSON 对象失败\n");
        cJSON_Delete(gen);
        return 1;
    }
    // 向 dataObj 添加 "key" 键值对，类型为 number，值为 1
    cJSON_AddNumberToObject(dataObj, "key", key);
    // 向 dataObj 添加 "val" 键值对，类型为 string，值为 "22.5"
    cJSON_AddStringToObject(dataObj, "val", value->valueint ? "true" : "false");
    // 将 dataObj 作为 "data" 的值添加到 gen 对象中
    cJSON_AddItemToObject(gen, "data", dataObj);
    // 序列化 JSON 对象为字符串
    char *jsonStr = cJSON_Print(gen);
    if (jsonStr == NULL)
    {
        printf("JSON 序列化失败\n");
        cJSON_Delete(gen);
        return -1;
    }
    printf("等待上传的JSON格式的字符串: %s\n", jsonStr);
    if (key > 300)
    {
        if (msg_queue_send("msg1", jsonStr, strlen(jsonStr), 0) < 0)
        {
            printf("发送消息队列失败！！！\n");
        }
    }
    else
    {
        if (msg_queue_send("msg", jsonStr, strlen(jsonStr), 0) < 0)
        {
            printf("发送消息队列失败！！！\n");
        }
    }
    printf("发送消息队列成功！！！\n");
    cJSON_Delete(gen); // 删除树
    return 1;
}

/**
 * @brief 处理get和post带参数的请求
 * @param sock：accepted 套接字  query_string：GET请求的参数 ，  input：Post请求的参数
 * @return int 类型的返回值
 */
////////////目前只做处理post下的请求数据。
int parse_and_process(int sock, const char *query_string, const char *input)
{
    // 这里的input是json格式的请求
    printf("input = %s\n", input);
    // 将网页传来的Json反序列化
    cJSON *root = cJSON_Parse(input);
    if (NULL == root)
    {
        printf("parse err\n");
        return -1;
    }
    cJSON *item1 = cJSON_GetObjectItem(root, "key");
    // 创建swich case 判断 到底下发的是哪种类型的命令：
    printf("key = %d\n", item1->valueint);
    // return mymodbusset(sock, root);
    get_jsondata(sock, item1->valueint, root);

    cJSON_Delete(root); // 删除树

    // 构建要回复的JSON数据
    // 发送HTTP响应给客户端
    return 0;
}
